package M

import (
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/gorilla/websocket"
)

func wsHandle(s *S, f func(*S)) {
	// 应答客户端告知升级连接为websocket
	wsSocket, err := wsUpgrader.Upgrade(s.Res, s.Req, nil)
	if err != nil {
		log.Println("websocket开启失败")
		return
	}
	s.WsCon = &wsConnection{
		Id:        RandStr(8),
		wsSocket:  wsSocket,
		inChan:    make(chan string, 1000),
		outChan:   make(chan string, 1000),
		closeChan: make(chan byte),
		isClosed:  false,
		f:         f,
	}
	//添加到主SERVER
	s.wsAdd()
	s.WsMsg = &wsMessage{
		Type: 1,
	}
	f(s)
	// 处理器,然后回调
	go s.wsProcLoop()
	// 读协程
	go s.wsReadLoop()
	// 写协程
	go s.wsWriteLoop()

}
func (s *S) WsWrite(data string) error {
	select {
	case s.WsCon.outChan <- data:
	case <-s.WsCon.closeChan:
		return errors.New("websocket closed")
	}
	return nil
}

func (s *S) WsRead() (string, error) {
	select {
	case msg := <-s.WsCon.inChan:
		return msg, nil
	case <-s.WsCon.closeChan:
	}
	return "", errors.New("websocket closed")
}

func (s *S) wsClose() {
	s.WsCon.wsSocket.Close()
	s.WsCon.mutex.Lock()
	defer s.WsCon.mutex.Unlock()
	if !s.WsCon.isClosed {
		s.WsCon.isClosed = true
		s.wsDel()
		s.WsMsg = &wsMessage{
			Type: 3,
		}
		s.WsCon.f(s)
		close(s.WsCon.closeChan)

		log.Println("websocket 关闭")
	}
}

func (s *S) wsReadLoop() {
	for {
		// 读一个message
		_, data, err := s.WsCon.wsSocket.ReadMessage()
		if err != nil {
			goto error
		}
		// 放入请求队列
		select {
		case s.WsCon.inChan <- string(data):
		case <-s.WsCon.closeChan:
			goto closed
		}
	}
error:
	s.wsClose()
closed:
}

func (s *S) wsWriteLoop() {
	for {
		select {
		// 取一个应答
		case msg := <-s.WsCon.outChan:
			// 写给websocket
			if err := s.WsCon.wsSocket.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
				goto error
			}
		case <-s.WsCon.closeChan:
			goto closed
		}
	}
error:
	s.wsClose()
closed:
}

func (s *S) wsProcLoop() {
	// 启动一个gouroutine发送心跳
	go func() {
		for {
			time.Sleep(50 * time.Second)
			if err := s.WsWrite("ping"); err != nil {
				fmt.Println("ping fail")
				s.wsClose()
				break
			}
		}
	}()

	// 这是一个同步处理模型（只是一个例子），如果希望并行处理可以每个请求一个gorutine，注意控制并发goroutine的数量!!!
	for {
		msg, err := s.WsRead()
		if err != nil {
			log.Println("read fail")
			break
		}
		s.WsMsg = &wsMessage{
			2,
			msg,
		}
		s.WsCon.f(s)
	}
}
func (s *S) wsAdd() {
	Server.wsMutex.Lock()
	defer Server.wsMutex.Unlock()
	Server.Ws[s.WsCon.Id] = s.WsCon
}
func (s *S) wsDel() {
	Server.wsMutex.Lock()
	defer Server.wsMutex.Unlock()
	delete(Server.Ws, s.WsCon.Id)
}
